﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Forms;
using CloudShot.Utils.Drawings;
using DrawTools.Editor;
using DrawTools.Editor.CanvasFigures;

namespace DrawTools.Controls
{
  public sealed partial class CanvasControl : UserControl, IDrawingCanvas
  {
    private readonly GraphicsList _graphicsList = new GraphicsList();
    public event EventHandler<EventArgs> RequestShowPropertiesDialog;
    public event EventHandler<EventArgs> RequestHidePropertiesDialog;

    /// <summary>
    ///   Group selection rectangle. Used for drawing.
    /// </summary>
    public Rectangle NetRectangle { get; set; }

    /// <summary>
    ///   Flag is set to true if group selection rectangle should be drawn.
    /// </summary>
    public bool IsDrawNetRectangle { get; set; }

    public CanvasCrop Crop { get; set; }

    public GraphicsList GraphicsList
    {
      get { return _graphicsList; }
    }

    public bool CaptureMouse
    {
      get { return Capture; }
      set { Capture = value; }
    }

    /// <summary>
    /// Theme of the Editor. Used by tools and Properties window
    /// </summary>
    public EditorTheme Theme { get; private set; }

    public CanvasControl()
    {
      InitializeComponent();

      SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

      textBox.Visible = false;

      Theme = new EditorTheme();
    }

    public Image GetClippedImage()
    {
      if (Crop == null)
        return null;

      Rectangle rect = Crop.Rectangle;
      rect.Offset(1, 1);
      rect.Inflate(-1, -1);

      using (var bitmap = new Bitmap(rect.Right, rect.Bottom))
      {
        DrawToBitmap(bitmap, new Rectangle(0, 0, rect.Right, rect.Bottom));
        return bitmap.Clone(rect, PixelFormat.Format32bppArgb);
      }
    }

    public void SetCursor(Cursor cursor)
    {
      Cursor = cursor;
    }

    #region Event Handlers

    protected override void OnKeyDown(KeyEventArgs e)
    {
      base.OnKeyDown(e);

      if (e.KeyCode == Keys.Delete)
      {
        if (GraphicsList.DeleteSelection())
        {
          Refresh();
          InvokePropertiesDialog();
        }
      }
      else if (e.KeyCode == Keys.A && e.Control)
      {
        GraphicsList.SelectAll();
        Refresh();
      }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
      base.OnMouseMove(e);

      if (Crop == null)
        Refresh();
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
      base.OnMouseDown(e);

      if (textBox.Visible)
        HideTextBox();
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
      base.OnMouseUp(e);

      if (e.Button == MouseButtons.Left)
        InvokePropertiesDialog();
    }

    protected override void OnMouseDoubleClick(MouseEventArgs e)
    {
      base.OnMouseDoubleClick(e);

      foreach (CanvasText textFigure in GraphicsList.OfType<CanvasText>())
      {
        if (textFigure.Rectangle.Contains(e.Location))
        {
          textFigure.IsSelected = false;

          //textBox.AcceptsReturn = true;
          textBox.Tag = textFigure;
          textBox.Location = textFigure.Rectangle.Location;
          textBox.Size = textFigure.Rectangle.Size;
          textBox.Text = textFigure.Text;
          textBox.Font = new Font(textBox.Font.FontFamily, textFigure.FontSize);
          textBox.TextAlign = ConvertAlign(textFigure.TextAlignment);
          textBox.Visible = true;
          textBox.Focus();
          return;
        }
      }
    }

    /// <summary>
    ///   Draw graphic objects and
    ///   group selection rectangle (optionally)
    /// </summary>
    private void OnCanvasPaint(object sender, PaintEventArgs e)
    {
      //e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
      e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

      _graphicsList.DrawFigures(e.Graphics);

      DrawCrop(e.Graphics);

      _graphicsList.DrawTrackers(e.Graphics);

      DrawNetSelection(e.Graphics);
    }

    private void OnTextBoxLostFocus(object sender, EventArgs e)
    {
      HideTextBox();
    }

    #endregion

    #region Events Invoke

    private void InvokePropertiesDialog()
    {
      if (!GraphicsList.SelectedObjects.Any())
      {
        InvokeRequestHidePropertiesDialog();
        return;
      }

      InvokeRequestShowPropertiesDialog();
    }

    private void InvokeRequestShowPropertiesDialog()
    {
      EventHandler<EventArgs> handler = RequestShowPropertiesDialog;
      if (handler != null)
        handler(this, EventArgs.Empty);
    }

    private void InvokeRequestHidePropertiesDialog()
    {
      EventHandler<EventArgs> handler = RequestHidePropertiesDialog;
      if (handler != null)
        handler(this, EventArgs.Empty);
    }

    #endregion

    #region Other Functions

    public IEnumerable<CanvasObject> GetObjectsAtPoint(Point point)
    {
      return GraphicsList.Where(t => t.HitTest(point) >= 0);
    }

    private static HorizontalAlignment ConvertAlign(StringAlignment align)
    {
      switch (align)
      {
        case StringAlignment.Near:
          return HorizontalAlignment.Left;
        case StringAlignment.Center:
          return HorizontalAlignment.Center;
        case StringAlignment.Far:
          return HorizontalAlignment.Right;
      }

      return HorizontalAlignment.Left;
    }

    private void DrawCrop(Graphics graphics)
    {
      if (Crop != null)
      {
        Crop.Draw(graphics);
        Crop.DrawTracker(graphics);
      }
      else
      {
        using (var brush = new SolidBrush(Color.FromArgb(120, Color.Black)))
        {
          graphics.FillRectangle(brush, graphics.ClipBounds);

          Point point = PointToClient(Cursor.Position);

          string label = string.Format("{0} x {1}", point.X, point.Y);
          graphics.DrawTextUnderCursor(label, new Point(point.X + 1, point.Y + 1), SystemFonts.DefaultFont, (int)graphics.ClipBounds.Width,
                                       (int)graphics.ClipBounds.Height, true);
        }
      }
    }

    /// <summary>
    ///   Draw group selection rectangle
    /// </summary>
    /// <param name="g"></param>
    private void DrawNetSelection(Graphics g)
    {
      if (!IsDrawNetRectangle)
        return;

      ControlPaint.DrawFocusRectangle(g, NetRectangle, Color.Black, Color.Transparent);
    }

    private void HideTextBox()
    {
      textBox.Visible = false;
      var textFigure = textBox.Tag as CanvasText;

      if (textFigure != null)
        textFigure.Text = textBox.Text;

      textBox.Tag = null;
    }

    #endregion
  }
}